/*
 * Copyright 2006-2008 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.martinimix.service.payment.provider.cybersource.context;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

import net.martinimix.domain.account.Address;
import net.martinimix.domain.account.Customer;
import net.martinimix.domain.order.OrderLine;
import net.martinimix.domain.payment.CreditCard;

/**
 * Provides a CyberSource request suitable for requesting services
 * related to order processing.
 *  
 * @author Scott Rossillo
 *
 */
public class OrderPaymentAuthorizationRequest extends SimpleCyberSourceServiceRequest implements CyberSourceServiceRequest {
	
	private int orderLineCount = 0;
	private BigDecimal shippingAndHandlingAmount;
	
	/**
	 * Constructs a new CyberSource request.
	 */
	public OrderPaymentAuthorizationRequest() { }

	/**
	 * Sets the billing address for this request.
	 * 
	 * @param billingAddress the billing <code>Address</code>
	 * for this request
	 */
	public void setBillingAddress(Address billingAddress) {

		if(billingAddress == null) {
			throw new IllegalArgumentException("[billingAddress] cannot be null!");
		}

		super.put("billTo_firstName", billingAddress.getFirstName());
		super.put("billTo_lastName",  billingAddress.getLastName());
		super.put("billTo_street1", billingAddress.getAddress1());

		super.put("billTo_city", billingAddress.getCity());
		super.put("billTo_state", billingAddress.getStateCode());

		super.put("billTo_postalCode", billingAddress.getPostalCode());
		super.put("billTo_country", billingAddress.getCountry());

		if(billingAddress.getPhoneNumber() != null) {
			super.put("billTo_phoneNumber", billingAddress.getPhoneNumber().toString());
		}
	}
	
	/**
	 * Sets the current customer's IP address for this request.
	 * 
	 * @param ipAddress the customer's IP address 
	 */
	public void setClientIPAddress(String ipAddress) {
		if(ipAddress != null) {
			super.put("billTo_ipAddress", ipAddress);
		}
	}

	/**
	 * Sets the shipping address for this request.
	 * 
	 * @param shippingAddress the shipping <code>Address</code>
	 * for this request
	 */
	public void setShippingAddress(Address shippingAddress) {
		
		if(shippingAddress == null) {
			throw new IllegalArgumentException("[shippingAddress] cannot be null!");
		}
		
		super.put("shipTo_firstName", shippingAddress.getFirstName());
		super.put("shipTo_lastName",  shippingAddress.getLastName());
		super.put("shipTo_street1", shippingAddress.getAddress1());

		super.put("shipTo_city", shippingAddress.getCity());
		super.put("shipTo_state", shippingAddress.getStateCode());

		super.put("shipTo_postalCode", shippingAddress.getPostalCode());
		super.put("shipTo_country", shippingAddress.getCountry());
	    
	    if(shippingAddress.getPhoneNumber() != null) {
	    	super.put("shipTo_phoneNumber", shippingAddress.getPhoneNumber().toString());
	    }
	}

	/**
	 * Sets the credit card for this request.
	 * 
	 * @param creditCard
	 */
	public void setCreditCard(CreditCard creditCard) {
		
		if(creditCard == null) {
			throw new IllegalArgumentException("[creditCard] cannot be null!");
		}
		
		super.put("card_accountNumber", creditCard.getCardNumber());
		if (creditCard.getCardSecurityCode() != null) {
			super.put("card_cvNumber", creditCard.getCardSecurityCode());
		}
		super.put("card_expirationMonth", String.valueOf(creditCard.getExpirationMonth()));
		super.put("card_expirationYear", String.valueOf(creditCard.getExpirationYear()));
		super.put("purchaseTotals_currency", "USD" );
		
	}

	/**
	 * Sets the customer for this request.
	 * 
	 * @param customer
	 */
	public void setCustomer(Customer customer) {
		super.put("billTo_email", customer.getEmailAddress());
	}

	/**
	 * Sets the order lines for this request.
	 * 
	 * @param orderLines
	 */
	public void setOrderLines(List orderLines) {
		
		OrderLine orderLine;
		
		this.orderLineCount = orderLines.size();

		for(int i = 0; orderLines != null && i < orderLines.size(); i++) {
		
			orderLine = (OrderLine) orderLines.get(i);
			super.put("item_" + i + "_unitPrice", orderLine.getUnitPrice().toString());
			super.put("item_" + i + "_quantity", String.valueOf(orderLine.getQuantity()));
			super.put("item_" + i + "_productName", orderLine.getProductCode());
		}
	}

	/**
	 * Sets the merchant reference code for this request.
	 * 
	 * @param merchantReferenceCode the merchant reference code for this request
	 */
	public void setMerchantReferenceCode(String merchantReferenceCode) {
		super.put(MERCHANT_REFERENCE_CODE_KEY, merchantReferenceCode);
	}
	


	/**
	 * Sets the total purchase amount for this request.
	 * 
	 * <p>
	 * Setting the purchase total amount is optional as long as line
	 * items have been set on this request.  Note, if both line items
	 * and a purchase total have been specified, the purchase total will be used.
	 * </p>
	 * 
	 * <p>
	 * See "Using a Grand Total" in 
	 * <a href="http://apps.cybersource.com/library/documentation/dev_guides/CC_Svcs_IG/html/">
	 * CyberSource's Credit Card Services Implementation Guide</a>.
	 * </p>
	 * 
	 * @param purchaseTotalAmount a <code>BigDecimal</code> containing the total purchase
	 * amount for this request
	 * 
	 * @param currencyCode the ISO currency code for the given <code>purchaseTotalAmount</code>
	 */
	public void setPurchaseTotalAmount(BigDecimal purchaseTotalAmount, String currencyCode) {
		super.put("purchaseTotals_grandTotalAmount", purchaseTotalAmount.toString());
		super.put("purchaseTotals_currency", currencyCode != null ? currencyCode : "USD");
	}
	
	/**
	 * @param currencyCode
	 */
	public void setPurchaseTotalsCurrencyCode(String currencyCode) {
		super.put(PURCHASE_TOTALS_CURRENCY_CODE_KEY, currencyCode != null ? currencyCode : "USD");
		
	}

	public void setShippingAndHandlingAmount(BigDecimal shippingAndHandlingAmount) {
		this.shippingAndHandlingAmount = shippingAndHandlingAmount;		
	}

	/**
	 * Returns a map containing the key, value pairs for this request.
	 * 
	 * @throws IllegalStateException if manditory request fields are not set
	 * prior to invoking this method
	 * 
	 * @see net.martinimix.service.payment.provider.cybersource.context.SimpleCyberSourceServiceRequest#toMap()
	 */
	public Map toMap() {

		int itemNum = orderLineCount;
		if(shippingAndHandlingAmount != null) {
			super.put("item_" + itemNum + "_unitPrice", shippingAndHandlingAmount.toString());
			super.put("item_" + itemNum + "_productCode", "shipping_and_handling");
			
		}
		
		this.validate();
		return super.toMap();

	}
	
	/**
	 * Validates that mandatory request fields are set.
	 * 
	 * 
	 */
	private final void validate() {
		
		if(super.get(MERCHANT_REFERENCE_CODE_KEY) == null) {
			throw new IllegalStateException("[" + MERCHANT_REFERENCE_CODE_KEY + "] must be set!");
		}
		
		if(super.get(PURCHASE_TOTALS_CURRENCY_CODE_KEY) == null) {
			throw new IllegalStateException("[" + PURCHASE_TOTALS_CURRENCY_CODE_KEY + "] must be set!");
		}
	}

	

}
